package com.andova.component.net;

import android.content.Context;
import android.net.ConnectivityManager;

import androidx.annotation.NonNull;

import com.google.gson.JsonSyntaxException;

import java.lang.ref.WeakReference;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;

import javax.net.ssl.SSLHandshakeException;

import retrofit2.Call;
import retrofit2.Response;

import static com.andova.CodeExtKt.ERROR_CONNECT;
import static com.andova.CodeExtKt.ERROR_ILLEGAL_ARGUMENT;
import static com.andova.CodeExtKt.ERROR_JSON_SYNTAX;
import static com.andova.CodeExtKt.ERROR_NO_NETWORK;
import static com.andova.CodeExtKt.ERROR_NO_ROUTE_TO_HOST;
import static com.andova.CodeExtKt.ERROR_SSL_HANDSHAKE;
import static com.andova.CodeExtKt.ERROR_TIMEOUT;
import static com.andova.CodeExtKt.ERROR_UNKNOWN;
import static com.andova.CodeExtKt.ERROR_UNKNOWN_HOST;
import static com.andova.CodeExtKt.ERROR_URL2SERVER;
import static com.andova.CodeExtKt.RESPONSE_EMPTY;
import static com.andova.CodeExtKt.RESPONSE_SUCCESS;

/**
 * Created by Administrator on 2019-02-12.
 *
 * @author kzaxil
 * @since 0.0.4
 */
public class RetrofitCallback2<T extends ResponseModel> implements retrofit2.Callback<T>, AndovaNetCall2<T, Call<T>> {
    private Call<T> mCall;
    private AndovaNetCallback2<T> mCallback;
    private WeakReference<Context> mContext;

    @Override
    public void onResponse(@NonNull Call<T> call, @NonNull Response<T> response) {
        try {
            mCallback.response(mContext, call, response);
            T body = response.body();
            if (!response.isSuccessful() || body == null || !body.isSuccess()) {
                mCallback.error(mContext, response);
                mCallback.complete(mContext, ERROR_URL2SERVER);
                return;
            }
            if (body.isEmpty()) {
                mCallback.complete(mContext, RESPONSE_EMPTY);
                return;
            }
            mCallback.success(mContext, response.body());
            mCallback.complete(mContext, RESPONSE_SUCCESS);
        } catch (Exception e) {
            onFailure(call, e);
        }
    }

    @Override
    public void onFailure(@NonNull Call<T> call, @NonNull Throwable throwable) {
        mCallback.failure(mContext, throwable, call);
        if (throwable instanceof JsonSyntaxException) {
            mCallback.complete(mContext, ERROR_JSON_SYNTAX);
        } else if (throwable instanceof SocketTimeoutException) {
            mCallback.complete(mContext, ERROR_TIMEOUT);
        } else if (throwable instanceof ConnectException) {
            mCallback.complete(mContext, ERROR_CONNECT);
        } else if (throwable instanceof SSLHandshakeException) {
            mCallback.complete(mContext, ERROR_SSL_HANDSHAKE);
        } else if (throwable instanceof NoRouteToHostException) {
            mCallback.complete(mContext, ERROR_NO_ROUTE_TO_HOST);
        } else if (throwable instanceof UnknownHostException) {
            mCallback.complete(mContext, ERROR_UNKNOWN_HOST);
        } else if (throwable instanceof IllegalArgumentException) {
            mCallback.complete(mContext, ERROR_ILLEGAL_ARGUMENT);
        } else {
            mCallback.complete(mContext, ERROR_UNKNOWN);
        }
    }

    @Override
    public void request(@NonNull WeakReference<Context> context, AndovaNetCallback2<T> callback, Call<T> call) {
        if (!isOpenNetwork(context.get())) {
            callback.complete(context, ERROR_NO_NETWORK);
            return;
        }
        mCall = call;
        mContext = context;
        mCallback = callback;
        callback.prepare(context);
        try {
            // todo fix ‘java.lang.OutOfMemoryError: pthread_create (1040KB stack) failed: Try again’
            call.enqueue(this);
        } catch (Exception ex) {
            onFailure(call, ex);
        }
    }

    @Override
    public Call<T> param() {
        return mCall;
    }

    @Override
    public String version() {
        return "2.4.0";
    }

    /**
     * 判断是否有网络可用
     */
    private boolean isOpenNetwork(Context cxt) {
        if (cxt == null) return false;
        ConnectivityManager cm = (ConnectivityManager) cxt.getSystemService(Context.CONNECTIVITY_SERVICE);
        return cm != null && cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isAvailable();
    }
}
